<?php

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 * Description of adminLayer
 *
 * @author mapcache
 */
require_once APPPATH . 'dao/configurationManager.php';
require_once APPPATH . 'controllers/manageLayer.php';

class adminLayer extends CI_Controller {

    function __construct() {
        parent::__construct();
    }

    private static $OPERATIONS = array(
        0 => 'Generar Teselas',
        1 => 'Regenerar Teselas',
        2 => 'Eliminar Teselas'
    );

    public function getOperations() {
        return adminLayer::$OPERATIONS;
    }

    public function index($layer = '', $srs = '') {
        if ($this->session->userdata('logged_in')) {
            $srs = urldecode($srs);
            $this->initView($layer, $srs);
        } else {
            redirect('/login');
        }
    }

    /*
     * Se encarga de cargar las vistas correspondientes al formulario de búsqueda
     * y la respuesta de la búsqueda
     */

    public function initView($layer, $srs) {
        //Se obtienen los datos para crear los campos del formulario
        $data = $this->getFields($layer, $srs);


        add_js(array('assets/libs/jqGrid/js/i18n/grid.locale-es.js', 'assets/libs/jqGrid/js/jquery.jqGrid.min.js',
            'assets/js/adminLayer.js'));
        add_css(array(
            '/assets/libs/jqGrid/css/ui.jqgrid.css',
            '/assets/css/adminLayer.css'));

        $this->load->view('pages/header');
        $this->load->view('pages/adminLayer.php', $data);
        $this->load->view('pages/footer');
    }

    /**
     * Se utiliza para obtener los grid el los zoom limits y todos los demás parámetros para inicializar 
     * los campos del form nueva tarea
     * @param type $layer
     */
    public function getSelectedLayerData($layer) {
        $layerData = ConfigurationManager::getInstance()->getTilesetGrids($layer);
        $layerData['resolutions'] = array();
        foreach ($layerData['grids'] as $grid) {
            $gridData = ConfigurationManager::getInstance()->getGridData((String) $grid);
            $layerData['resolutions'][$gridData['name']] = $gridData['resolutions'];
        }
        echo json_encode($layerData);
    }

    /*
     * Retorna arreglos con los parámetros necesarios para crear los campos del
     * formulario.
     * Dichos parámetros son pasados a métodos del Form Helper de CodeIgniter
     */

    private function getfields($layer, $srs) {
        
        $data['grids'] = ConfigurationManager::getInstance()->getSupportedGrids();
        $data['grid'] = $srs;
        $tilesets = ConfigurationManager::getInstance()->getConfiguredTileSets();
        asort($tilesets);
        $data['layers'] = $tilesets;
        $data['layer'] = $layer;
        $data['srs'] = $srs;
        
        /* Campo operation */
        $data['operation'] = $this->getOperations();

        /* Campo nThreads */
        $data['nThreads'] = array();
        $data['nThreads'][0] = '-';
        for ($i = 1; $i < MAX_THREADS; $i++) {
            $data['nThreads'][$i] = $i;
        }
        /* Campo nProcess */
        $data['nProcess'] = array();
        $data['nProcess'][0] = '-';
        for ($i = 1; $i < MAX_PROCESS; $i++) {
            $data['nProcess'][$i] = $i;
        }


       $data = $this->addMetatileFields($data);

        /* Campo minZoomLevel */
        $data['minZoomLevel'] = array();

        /* Campo maxZoomLevel */
        $data['maxZoomLevel'] = array();

        /* Extensión */
        /* Campo bboxMinX */

        $data['bboxMinX'] = array('name' => 'bboxMinX',
            'id' => 'bboxMinX',
            'value' => '',
            'maxlength' => 20,
            'size' => 30,
            'class' => 'bbox'
        );

        /* Campo bboxMaxX */
        $data['bboxMaxX'] = array(
            'name' => 'bboxMaxX',
            'id' => 'bboxMaxX',
            'value' => '',
            'maxlength' => 20,
            'size' => 30,
            'class' => 'bbox'
        );


        /* Campo bboxMinY */
        $data['bboxMinY'] = array(
            'name' => 'bboxMinY',
            'id' => 'bboxMinY',
            'value' => '',
            'maxlength' => 20,
            'size' => 30,
            'class' => 'bbox'
        );


        /* Campo bboxMaxY */
        $data['bboxMaxY'] = array(
            'name' => 'bboxMaxY',
            'id' => 'bboxMaxY',
            'value' => '',
            'maxlength' => 20,
            'size' => 30,
            'class' => 'bbox'
        );
        $data['regionPopUpAttr'] = ConfigurationManager::getInstance()->getRegionPopUpAttr();

        return $data;
    }

    public function getRunningTasksForInit() {

        $this->load->model('task', '', TRUE);
        $tasks = $this->task->getRunningTask();
        echo json_encode($tasks);
    }

    public function perform() {

        $layer = $this->input->post('layer');
        // no debe haber más de una tarea corriendo en cada capa
        if (!$this->existRunningTaskOnLayer($layer)) {

            try {
                $outputFile = $this->getOutputFileName($layer);
                $outputErrorFile = $this->getErrorFileName($layer);

                if (file_exists($outputFile))
                    unlink($outputFile);
                if (file_exists($outputErrorFile))
                    unlink($outputErrorFile);


                $descriptorspec = array(
                    0 => array("pipe", "r"),
                    1 => array("file", $outputFile, "w"),
                    2 => array("file", $outputErrorFile, "a")
                );

                $comando = $this->buildCommand();
                //Todo sacar el harcodeo del mapcachexml
                $process = proc_open('exec ' . $comando . ' &', $descriptorspec, $pipes, MAPCACHE_HOME, null);

                sleep(0.1); //duermo 100 milisegundos por las dudas
                if (is_resource($process)) {
                    $retVal = array();

                    $pidof = exec('pidof mapcache_seed', $retVal);

                    $realpid = explode(" ", $pidof); // me trae el último PID

                    $metatileX = $this->input->post('metatileX');
                    $metatileY = $this->input->post('metatileY');
                    $layerData = ConfigurationManager::getInstance()->getTileSetDataByName($layer);
                            
//                    if($metatileX <= 0 && $metatileY <=0) {
//                        $metatile = explode(' ',trim($layerData['metatile']));
//                        print_r($layerData['metatile']);
//                        //$tilesPerRequest = $metatile[0] * $metatile[1];
//                       
//                    }
                    $this->load->model('task', '', TRUE);
                    $task = $this->task->createTask($realpid[0], $layer, 1, 1, $metatileX, $metatileY);
                    // devuelvo la fila recien insertada para agregarla a la tabla.
                    proc_close($process);
                    echo json_encode($task);
                }
            } catch (Exception $e) {
                echo json_encode(false); //devolver algo distinto
            }
        } else {
            echo json_encode(false); //devuelvo false a la llamada si ya existia una tarea
        }
    }

    public function checkStatus($taskID, $pid, $layer) {

        $outputFileName = $this->getOutputFileName($layer);
        $outputErrorFileName = $this->getErrorFileName($layer);

        $this->load->model('task', '', TRUE);

        /**
         * Tomo la fecha de modificación del archivo de salida
         * 1) si el PID no está corriendo la taréa terminó bien
         * 2) si el PID está corriendo , pero la fecha de modificación del archivo es igual 
         *   a la última vez que se analizó (intento n veces) . Significa que se colgó y cancelo la 
         * 3) si el pid está corriendo 
         */
        $outputModifiedDate = date('Y-m-d H:i:s', filemtime($outputFileName));
        $outputFileText = file_get_contents($outputFileName);
        $countMetatiles = substr_count($outputFileText, "seeding tile");

        $outputErrorFileText = file_get_contents($outputErrorFileName);
        $errors = substr_count($outputErrorFileText, 'ServiceExceptionReport');
        $task = $this->task->getTaskByID($taskID);
        //actualizo la cantidad de teselas 
        $this->task->updateTask($taskID, $countMetatiles, $task->tilesPerRequest);
        if ($task && $this->isPidRunning($pid)) { // sino me fijo si esta corriendo sino la finalizo con éxito 
            if ($task->lastOutputModification >= $outputModifiedDate) {

                if ($task->intents > TASK_MAX_INTENTS) {
                    $this->task->finishTask($taskID, $pid, 4); // finalizar la tarea con error
                    exec('kill -9 ' . $pid); // matar el pid
                } else {
                    $task->intents = $task->intents + 1;
                    $task->updateTaskIntents($task->intents);
                }
            } else if ($errors > 0) {
                $this->task->finishTask($taskID, $pid, 4); // finalizar la tarea con error
                exec('kill -9 ' . $pid); // matar el pid
            } else {
                // todavía esta corriendo y generando teselas
                $this->task->updateTask($taskID, $countMetatiles, $task->tilesPerRequest);
            }
        } else if ($errors > 0) {

            $this->task->finishTask($taskID, $pid, 4); // Finalizado
        } else {
            $this->task->finishTask($taskID, $pid, 2); // Finalizado
        }
        // la vuelvo a pedir por si modifiqué algo
        echo json_encode($this->task->getTaskByID($taskID));
    }

    public function stopTask($taskID, $pid) {

        if ($this->isPidRunning($pid)) {
            $rdo = exec('kill -9 ' . $pid);
            $this->load->model('task', '', TRUE);
            $this->task->cancelTask($taskID, $pid);
            echo json_encode($this->task->getTaskByID($taskID));
        }
    }

    private function isPidRunning($pid) {

        $stat = exec('ps -p ' . $pid . ' | grep ' . $pid);
        return $stat != '';
    }

    private function existRunningTaskOnLayer($layer) {
        $this->load->model('task', '', TRUE);
        $task = $this->task->isTaskRunningOnLayer($layer);
        return !empty($task);
    }

    private function buildCommand() {
        $layer = $this->input->post('layer');
        $grid = $this->input->post('grid');
        $nThreads = $this->input->post('nThreads');
        $nProcess = $this->input->post('nProcess');
        $operation = $this->input->post('operation');
        $metatileX = $this->input->post('metatileX');
        $metatileY = $this->input->post('metatileY');
        $minZoomLevel = $this->input->post('minZoomLevel');
        $maxZoomLevel = $this->input->post('maxZoomLevel');
        // hay que validar que el bBox esté dentro del bBox de la capa
        $bboxMinX = $this->input->post('bboxMinX');
        $bboxMinY = $this->input->post('bboxMinY');
        $bboxMaxX = $this->input->post('bboxMaxX');
        $bboxMaxY = $this->input->post('bboxMaxY');

        $comando = 'mapcache_seed -c ' . MAPCACHEXML . ' -t ' . $layer . ' -g ' . $grid . ' -z ' . $minZoomLevel . ',' . $maxZoomLevel;

        if ($metatileX > 0 && $metatileY > 0) {
            $comando . ' -M ' . $metatileX . ',' . $metatileY;
        }

        // nro de hilos o nro de procesos (son mutamente excluyentes)
        if ($nThreads > 0) {
            $comando = $comando . ' -n ' . $nThreads;
        } else if ($nProcess > 0) {
            $comando = $comando . ' -p ' . $nProcess;
        }
        // bbox
        if (($bboxMinX) && ($bboxMinY) && ($bboxMaxX) && ($bboxMaxY)) {
            $comando = $comando . ' -e ' . $bboxMinX . ',' . $bboxMinY . ',' . $bboxMaxX . ',' . $bboxMaxY;
        }
        // tipo de operación
        $OPERATIONS = $this->getOperations();
        switch ($operation) {
            case $OPERATIONS[1]:
                $comando = $comando . ' -o now';
                break;
            case $OPERATIONS[2]:
                $comando = $comando . ' -m delete';
                break;
            default :
                break;
        }

        return $comando;
    }

    private function getOutputFileName($layer) {
        //echo( OUTPUTS_DIRECTORY . OUTPUT_SEED_PREFIX . '_' . $layer . '_' . OUTPUT_SEED_SUFIX . OUTPUTFILE_EXTENSION);
        return OUTPUTS_DIRECTORY . OUTPUT_SEED_PREFIX . '_' . $layer . '_' . OUTPUT_SEED_SUFIX . OUTPUTFILE_EXTENSION;
    }

    private function getErrorFileName($layer) {
        return OUTPUTS_DIRECTORY . OUTPUT_SEED_PREFIX . '_' . $layer . '_' . ERROR_SEED_SUFIX . OUTPUTFILE_EXTENSION;
    }

    private function addMetatileFields($data) {
         /* Campo metatileX */
        $data['metatileX'] = array();
        $data['metatileX'][0] = '-';
//        $data['metatileX'][1] = 2;
//        $data['metatileX'][2] = 4;
//        /* Campo metatiley */
        $data['metatileY'] = array();
        $data['metatileY'][0] = '-';
//        $data['metatileY'][1] = 2;
//        $data['metatileY'][2] = 4;
        for ($i = 0; $i <= intval(MAX_METATILE); $i++) {
            $metatile = pow(2, $i);
            $data['metatileX'][$i + 1] = $metatile;
            $data['metatileY'][$i + 1] = $metatile;
        }
        return $data;
    }
    
     public function getGridData($srs) {
        $data = array();
        $data['grid'] = ConfigurationManager::getInstance()->getGridData(urldecode($srs));
        echo json_encode($data);
    }

}

?>
